#include "stdafx.h"
#include "cppflibinc.h"
#include "TestMiscFilePath.h"

using namespace cppflib;
using namespace cppflib::os;
using namespace cppflib::json;
using namespace cppflib::io;

class MyJSONHandler : public JSONHandler
{
private:
    void PrintIndentation()
    {
       for (int i = 0; i < this->GetObjNestedLevel(); ++i)
          debug::PlainPrint(_S("  "));

       for (int i = 0; i < this->GetArrayNestedLevel() + 1; ++i)
          debug::PlainPrint(_S("  "));
    }

public:
    MyJSONHandler() { }
    virtual ~MyJSONHandler() { }

    virtual void StartObject() 
    {
       debug::PlainPrint(_S("\n")); 
       PrintIndentation(); 
       debug::PlainPrint(_S("{\n")); 
    }
    virtual void EndObject()
    {
       PrintIndentation(); 
       debug::PlainPrint(_S("}\n")); 
    }
    virtual void StartNVPair(const CFString & nvPairName) 
    {
       PrintIndentation(); 
       debug::PlainPrint(_S("\"%s\":"), static_cast<pcwstr_t>(nvPairName)); 
    }
    virtual void ValueString(const CFString & v) {debug::PlainPrint(_S("\"%s\""), static_cast<pcwstr_t>(v)); }
    virtual void ValueInt(const int v) { debug::PlainPrint(_S("%d"), v); }
    virtual void ValueInt64(const i64_t v) { debug::PlainPrint(_S("%lld"), v); }
    virtual void ValueDouble(const double v) { debug::PlainPrint(_S("%.8f"), v); }
    virtual void ValueBoolean(const bool v) { debug::PlainPrint(_S("%s"), v ? _S("true") : _S("false")); }
    virtual void ValueNull() { debug::PlainPrint(_S("null")); }
    virtual void EndNVPair() { debug::PlainPrint(_S("\n")); }
    virtual void StartArray() 
    {  
       debug::PlainPrint(_S("\n")); 
       PrintIndentation();
       debug::PlainPrint(_S("  [\n"));
    }
    virtual void EndArray() 
    { 
       debug::PlainPrint(_S("\n"));
       PrintIndentation();
       debug::PlainPrint(_S("  ]\n"));
    }

    virtual void Error(const CFString &msg) { debug::PlainPrint(_S("error: %s\n"), static_cast<pcwstr_t>(msg)); }
    virtual void FatalError(const CFString &msg) { debug::PlainPrint(_S("fatal error: %s\n"), static_cast<pcwstr_t>(msg)); }
    virtual void Warning(const CFString &msg) { debug::PlainPrint(_S("warning: %s\n"), static_cast<pcwstr_t>(msg)); }
};

class MyJSONHandler2 : public JSONHandler
{
public:
    MyJSONHandler2() { }
    virtual ~MyJSONHandler2() { }

    virtual void StartObject() 
    {
    }
    virtual void EndObject()
    {
    }
    virtual void StartNVPair(const CFString & nvPairName) 
    {
    }
    virtual void ValueString(const CFString & v) { }
    virtual void ValueInt(const int v) { }
    virtual void ValueInt64(const i64_t v) { }
    virtual void ValueDouble(const double v) { }
    virtual void ValueBoolean(const bool v) { }
    virtual void ValueNull() { }
    virtual void EndNVPair() { }
    virtual void StartArray() 
    {  
    }
    virtual void EndArray() 
    { 
    }

    virtual void Error(const CFString &msg) { /*debug::PlainPrint(_S("error: %s\n"), static_cast<pcwstr_t>(msg));*/ }
    virtual void FatalError(const CFString &msg) { debug::PlainPrint(_S("fatal error: %s\n"), static_cast<pcwstr_t>(msg)); }
    virtual void Warning(const CFString &msg) { debug::PlainPrint(_S("warning: %s\n"), static_cast<pcwstr_t>(msg)); }
};

static FileStruct jsonFiles[] = {
                        {JSON_PATH_PREFIX _S("test_1.json"), true, true},
                        {JSON_PATH_PREFIX _S("test_2.json"), true, true},
                        {JSON_PATH_PREFIX _S("test_3.json"), true, true},
                        {JSON_PATH_PREFIX _S("test_4.json"), true, true},
                        {JSON_PATH_PREFIX _S("test_5.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_6.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_7.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_8.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_9.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_10.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_11.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_12.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_13.json"), true, true},
                        {JSON_PATH_PREFIX _S("test_13_a.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_14.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_15.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_16.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_17.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_18.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_19.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_20.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_21.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_22.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_23.json"), true, true},
                        {JSON_PATH_PREFIX _S("test_24.json"), false, true},
                        {JSON_PATH_PREFIX _S("test_25.json"), false, true},
                        {NULL, false, false}
                               };

static void TestParseJSON()
{
   cppmempool::MemPoolWrap memPool;

   JSONQuickParser parser;

   for (int i = 0; jsonFiles[i].filePath && jsonFiles[i].involveInFullTest; ++i) {
      //debug::PlainPrint(_S(">> Parsing %s\n"), jsonFiles[i].filePath);
      JSONHandler *pHandler = new MyJSONHandler2();
      parser.Parse(jsonFiles[i].filePath, *pHandler);

      
      if (pHandler->IsErrorMarked() != !jsonFiles[i].shouldParseOK) {
         debug::Print(debug::DL_INFO, _S("Error parsing %s\n"), jsonFiles[i].filePath);
         assert(false);
      }
   }

}

static void TestParseJSON_2()
{
   cppmempool::MemPoolWrap memPool;

   JSONQuickParser parser(false); // don't mark error for duplicated names

   JSONHandler *pHandler = new MyJSONHandler2();
   parser.Parse(JSON_PATH_PREFIX _S("test_24.json"), *pHandler);
   assert(!pHandler->IsErrorMarked());

   pHandler = new MyJSONHandler2();
   parser.Parse(JSON_PATH_PREFIX _S("test_25.json"), *pHandler);
   assert(!pHandler->IsErrorMarked());
}

static void TestParseJSON_3()
{
   cppmempool::MemPoolWrap memPool;

   try {
      JSONDocument *pDoc = JSONDocument::Parse(JSON_PATH_PREFIX _S("test_1.json"));
      JSONObject *pObj = pDoc->GetRootJSONObject();
      assert(pObj->GetDirectChildCount() == 2);
      assert(pObj->GetValueType(0) == JSONNode::JVL_BOOLEAN);
      assert(pObj->GetValueType(2) == JSONNode::JVL_NULL);
      assert(pObj->GetValueType(3) == JSONNode::JVL_INT64);
      assert(pObj->GetValueType(5) == JSONNode::JVL_OBJECT);
      assert(pObj->GetValueType(6) == JSONNode::JVL_ARRAY);
      assert(pObj->GetValueType(CFString(_S("test5"))) == JSONNode::JVL_DOUBLE);
      assert(pObj->GetValueType(CFString(_S("stbc_server_host"))) == JSONNode::JVL_STRING);
      assert(pObj->GetNumberOfNameValuePairs() == 10);
      assert(pObj->Contains(CFString(_S("stbc_secure_type"))));
      assert(!pObj->Contains(CFString(_S("dfdfs"))));
      assert(pObj->IsNull(CFString(_S("test3"))));
      assert(!pObj->IsNull(CFString(_S("epg_server_host"))));
      assert(pObj->Remove(CFString(_S("stbc_secure_type"))));
      assert(pObj->GetNumberOfNameValuePairs() == 9);

      JSONNode *pNode = static_cast<JSONNode*>(pObj->GetChild(0));
      assert(pNode->GetNodeType() == JSONNode::JN_OBJECT);
      JSONObject *pSubObj = pNode->CastToJSONObject();

      assert(pSubObj->GetDirectChildCount() == 0);
      assert(pSubObj->GetNumberOfNameValuePairs() == 2);
      assert(pSubObj->GetValueType(0) == JSONNode::JVL_STRING);
      assert(pSubObj->GetValueType(1) == JSONNode::JVL_INT32);
      assert(pSubObj->GetInt32(CFString(_S("port"))) == 8080);
      assert(pSubObj->GetString(CFString(_S("host")))->Compare(_S("10.11.12.179")) == 0);
      assert(pSubObj->GetParent() == pObj);

      pNode = static_cast<JSONNode*>(pObj->GetChild(1));
      assert(pNode->GetNodeType() == JSONNode::JN_ARRAY);
      JSONArray *pJArray = pNode->CastToJSONArray();
      assert(pJArray->GetDirectChildCount() == 4);
      assert(pJArray->GetNumberOfValues() == 8);
      assert(pJArray->GetValueType(0) == JSONNode::JVL_OBJECT);
      assert(pJArray->GetValueType(1) == JSONNode::JVL_OBJECT);
      assert(pJArray->GetValueType(4) == JSONNode::JVL_INT32);
      assert(pJArray->GetValueType(5) == JSONNode::JVL_NULL);
      assert(pJArray->GetValueType(6) == JSONNode::JVL_BOOLEAN);
      assert(pJArray->GetInt32(4) == 1234);
      assert(pJArray->IsNull(5));
      assert(pJArray->GetParent() == pObj);
      assert(pJArray->Remove(7));
      assert(pJArray->GetNumberOfValues() == 7);

      pSubObj = static_cast<JSONObject*>(pJArray->Get(1));
      assert(pSubObj->GetString(CFString(_S("host")))->Compare(_S("10.3.222.100")) == 0);
      assert(pSubObj->GetParent() == pJArray);
   }
   catch (exceptions::BaseException & e) {
      debug::Print(debug::DL_ERROR, _S("Error parsing json -- %s\n"), e.GetMsg());
      assert(false);
   }
}

static void TestParseJSON_4()
{
   cppmempool::MemPoolWrap memPool;
   debug::Print(_S("Test parsing JSON using JSONDocument ...\n"));

   CFString fileName;

   for (int i = 0; jsonFiles[i].filePath && jsonFiles[i].involveInFullTest; ++i) {
      fileName = jsonFiles[i].filePath;

      if (jsonFiles[i].shouldParseOK) {
         JSONDocument *pDoc = JSONDocument::Parse(fileName);  // should be OK
      }
      else {
         try {
            JSONDocument *pDoc = JSONDocument::Parse(fileName);  // should fail
            debug::Print(debug::DL_ERROR, _S("Error should occur when parsing document -- %s\n"), static_cast<pcwstr_t>(fileName));
            assert(false);
         }
         catch(JSONException &e) {
            // OK
            e.GetMsg(); // to eliminate compiler warning
         }
         catch(exceptions::BaseException & e) {
            debug::Print(debug::DL_ERROR, _S("What is this exception ? -- %s (%s)\n"), static_cast<pcwstr_t>(fileName), e.GetMsg());
            assert(false);
         }
      }
   }

}

//#define _WRITETOSTRING

static Stream * __TestWriteJSON()
{
   cppmempool::MemPoolWrap memPool;

#ifdef _WRITETOSTRING
   StringWriter * pWriter = new StringWriter(256);
#else
   MemoryStream * pStream = new MemoryStream();
   StreamWriter * pWriter = new StreamWriter(pStream, text::CS_UTF8);
#endif
   JSONWriter * pJSONWriter = new JSONWriter(pWriter, true, false);

   try {
      pJSONWriter->Object()
         .Key(CFString("hello")).Value(CFString("OK"))
         .Key(CFString("yes"))
            .Object()
               .Key(CFString("hello")).Value(CFString("OK"))
               .Key(CFString("empty"))
                  .Array().EndArray()
               .Key(CFString("yes"))
                  .Array()         
                     .Object()
                        .Key(CFString("hello")).Value(CFString("OK"))
                        .Key(CFString("yes"))
                           .Array()
                              .Object()
                                 .Key(CFString("hello")).Value(CFString("OK1234567890abcdefghijkl\"mnopqrstuvwxyzOK1234567890\\abcdefghijklm/nopqrstuvwxyzOK1234567890abcdefghijklmno\\pqrstuvwxyzOK1234567890abcde\"fghijklmnopqrstuvwxyzOK1234567890abcdefghijklmnopqrs	uvwxyzOK1234567890abcdefghijklm\nopqrstuvwxyz"))
                                 .Key(CFString("yes"))
                                    .Object()
                                       .Key(CFString("hello")).Value(CFString("OK"))
                                       .Key(CFString("yes"))
                                          .Array()
                                             .Object()
                                                .Key(CFString("hello")).Value(CFString("OK"))
                                                .Key(CFString("yes")).Value(CFString("no"));
                                             
      CFString ts("content");
#ifndef _WRITETOSTRING
      ts += static_cast<wc_t>(0xd85f); // write UTF16 surrogate pairs
      ts += static_cast<wc_t>(0xdd44);
#endif
      pJSONWriter->Key(CFString("surrogateChars")).Value(ts);

      pJSONWriter->EndObject()
                  .Object()
                  .EndObject();
      pJSONWriter->EndDocument(); // close all objects and arrays
      pJSONWriter->Close();
#ifdef _WRITETOSTRING
      debug::PlainPrint(pWriter->GetStringBuffer());
#endif
         
   }
   catch(JSONException &e) {
      debug::Print(debug::DL_ERROR, _S("JSONWriter error -- %s"), e.GetMsg());
      assert(false);
   }

#ifdef _WRITETOSTRING
   return NULL;
#else
   pStream->ResetPointer(); // reset to beginning
   cppmempool::UnlinkFromPool(pStream); // return pStream, unlink from current pool
   return pStream;
#endif

}

static void TestWriteJSON()
{
   debug::Print(_S("Testing writing JSON...\n"));

   cppmempool::MemPoolWrap memPool;

#ifdef _WRITETOSTRING
   __TestWriteJSON();
#else
   Stream *pStream = __TestWriteJSON();
   memPool.AddMemBlock(pStream);
   
   try {
      StreamReader *pReader = new StreamReader(pStream, text::CS_UTF8); 
      JSONDocument *pDoc = JSONDocument::Parse(*pReader);  // should be OK
   }
   catch(exceptions::BaseException & e) {
      debug::Print(debug::DL_ERROR, _S("What is this exception ? (%s)\n"), e.GetMsg());
      assert(false);
   }
#endif
}

static void TestWriteJSON_2()
{
   cppmempool::MemPoolWrap memPool;
   
   MemoryStream * pStream = new MemoryStream();
   StreamWriter * pWriter = new StreamWriter(pStream, text::CS_UTF8);
   JSONWriter * pJSONWriter = new JSONWriter(pWriter, false, false);

   try {
      pJSONWriter->Array();   // only 'Object start' is valid when JSON document starts
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->Comment(CFString("This is comment")); // only 'Object start' is valid when JSON document starts
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->Key(CFString("new key"));  // only 'Object start' is valid when JSON document starts
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->Object();  // OK
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }

   try {
      pJSONWriter->Comment(CFString("comment before normal objects"));
      pJSONWriter->Key(CFString("newObject")).Value(CFString("new value"))
                  .Key(CFString("intObject")).Value(2234455)
                  .Key(CFString("boolObject")).Value(true)
                  .Key(CFString("nullObject")).ValueNull()
                  .Key(CFString("i64Object")).Value(I64N(39488384924542))
                  .Key(CFString("doubleObject")).Value(3.1412475);
      pJSONWriter->Comment(CFString("comment after normal objects"));
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }

   try {
      pJSONWriter->Key(CFString("intObject")).Value(2234455); // duplicated object
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg();  }

   try {
      pJSONWriter->Key(CFString("subObject"))
                   .Object();
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }

   try {
      pJSONWriter->Comment(CFString("comment with a */")); // comment cannot contains a '*' and '/'
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->Key(CFString("intObject")).Value(2234455); // OK      
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }

   try {
      pJSONWriter->Key(CFString("subArray"))
                  .Array();
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }

   try {
      pJSONWriter->ValueNull().Value(3424).Value(CFString("eeeeee dfdf"));
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }

   try {
      pJSONWriter->Key(CFString("keyInArray"));  // key not allowed in an array
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->EndObject();  // still in an array
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->Array();  // nested array
      pJSONWriter->EndArray();  // end array right away (empty array)
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }
   
   try {
      pJSONWriter->EndArray();
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }

   try {
      pJSONWriter->EndArray();  // no more array to end
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->EndObject();
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }

   try {
      pJSONWriter->Key(CFString("boolObject")).Value(false); // duplicated object
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg();  }

   try {
      pJSONWriter->Key(CFString("qqqq"));
      pJSONWriter->Key(CFString("dffd")); // key after key not valid
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg();  }

   try {
      pJSONWriter->Value(CFString("qqqq"));
      pJSONWriter->Value(CFString("dffd")); // value after value not valid
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg();  }

   try {
      pJSONWriter->Key(CFString("helloObject")).Value(488433);
      pJSONWriter->EndObject();
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }

   //////////////////////////// JSON document ends /////////////////////////

   try {
      pJSONWriter->Object();  // can't write anything after document ends
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->EndObject();  // can't write anything after document ends
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->Array();  // can't write anything after document ends
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->EndArray();  // can't write anything after document ends
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->ValueNull();  // can't write anything after document ends
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->Value(false);  // can't write anything after document ends
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->Key(CFString("keyOutside"));  // can't write anything after document ends
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->Comment(CFString("comment after doc"));  // can't write anything after document ends
      assert(false);
   }
   catch (JSONException &e) { e.GetMsg(); }

   try {
      pJSONWriter->EndDocument();  // this one is OK
      pJSONWriter->Flush();
      pJSONWriter->Close();
   }
   catch (JSONException &e) { e.GetMsg(); assert(false); }

   pStream->ResetPointer();

   // parse the stream finally
   try {
      StreamReader *pReader = new StreamReader(pStream, text::CS_UTF8); 
      JSONDocument *pDoc = JSONDocument::Parse(*pReader);  // should be OK
   }
   catch(exceptions::BaseException & e) {
      debug::Print(debug::DL_ERROR, _S("What is this exception ? (%s)\n"), e.GetMsg());
      assert(false);
   }
}

void TestJSON(void)
{
   debug::Print(_S("Testing JSON parsers...\n"));
   TestParseJSON();
   TestParseJSON_2();
   TestParseJSON_3();
   TestParseJSON_4();

   TestWriteJSON();
   TestWriteJSON_2();
}

 